using Sensor.UI.Models;
using System;
using System.Text;

namespace Sensor.UI.Processors;

/// <summary>
/// 文本协议分包器
/// 用于解析和处理特定格式的传感器数据帧
/// 数据帧格式：d0 x0 y0 z0 d1 x1 y1 z1 d2 x2 y2 z2 d3 x3 y3 z3\r\n
/// 其中 d0-d3 为传感器标识符，x/y/z 为对应的三轴坐标值
/// </summary>
public class TextProtocolFrameSplitter : IDisposable
{
    #region 常量定义
    /// <summary>
    /// 帧起始标记，标识数据帧的开始
    /// </summary>
    private const string FrameStartMarker = "d0";

    /// <summary>
    /// 帧结束标记，标识数据帧的结束
    /// </summary>
    private const string FrameEndMarker = "\r\n";

    /// <summary>
    /// 每个传感器数据项包含的元素数量（标识符 + X + Y + Z）
    /// </summary>
    private const int ItemsPerSensor = 4;

    /// <summary>
    /// 预期的传感器数量
    /// </summary>
    private const int ExpectedSensorCount = 4;

    /// <summary>
    /// 预期的数据项总数（传感器数量 × 每个传感器的数据项数量）
    /// </summary>
    private const int ExpectedDataItemCount = ExpectedSensorCount * ItemsPerSensor;
    #endregion

    #region 私有字段
    /// <summary>
    /// 用于存储接收到的数据的缓冲区
    /// </summary>
    private readonly StringBuilder _buffer = new();

    /// <summary>
    /// 标识是否已释放资源
    /// </summary>
    private bool _disposed;
    #endregion

    #region 公共属性
    /// <summary>
    /// 获取或设置文本编码，默认为UTF-8
    /// </summary>
    public Encoding Encoding { get; set; } = Encoding.UTF8;
    #endregion

    #region 事件
    /// <summary>
    /// 当接收到有效数据帧时触发
    /// </summary>
    /// <param name="frameText">原始帧文本</param>
    /// <param name="sensorInfo">解析后的传感器信息</param>
    public event Action<string, SensorInfo>? ValidFrameReceived;

    /// <summary>
    /// 当检测到无效数据帧时触发
    /// </summary>
    /// <param name="errorMessage">错误描述</param>
    /// <param name="frameText">原始帧文本（可能为空）</param>
    public event Action<string, string>? InvalidFrameDetected;
    #endregion

    #region 构造函数
    /// <summary>
    /// 初始化 TextProtocolFrameSplitter 类的新实例
    /// </summary>
    public TextProtocolFrameSplitter() { }

    /// <summary>
    /// 初始化 TextProtocolFrameSplitter 类的新实例，并指定文本编码
    /// </summary>
    /// <param name="encoding">文本编码</param>
    /// <exception cref="ArgumentNullException">当encoding为null时抛出</exception>
    public TextProtocolFrameSplitter(Encoding encoding)
    {
        Encoding = encoding ?? Encoding.UTF8;
    }
    #endregion

    #region 公共方法
    /// <summary>
    /// 处理接收到的字节数据
    /// </summary>
    /// <param name="data">接收到的字节数据</param>
    /// <exception cref="ArgumentNullException">当data为null时抛出</exception>
    public void ProcessReceivedData(byte[] data)
    {
        ArgumentNullException.ThrowIfNull(data);

        if (data.Length == 0) return;

        try
        {
            // 使用类属性指定的编码将字节数据转换为文本
            string text = Encoding.GetString(data);

            if (string.IsNullOrEmpty(text)) return;

            // 将文本添加到缓冲区并尝试解析完整帧
            _buffer.Append(text);
            ParseFrames();
        }
        catch (DecoderFallbackException ex)
        {
            // 将原始数据转换为Base64字符串以便调试
            string base64Data = Convert.ToBase64String(data);
            InvalidFrameDetected?.Invoke($"数据解码失败: {ex.Message}", base64Data);
        }
        catch (Exception ex)
        {
            // 捕获其他异常
            string base64Data = Convert.ToBase64String(data);
            InvalidFrameDetected?.Invoke($"数据处理失败: {ex.Message}", base64Data);
        }
    }

    /// <summary>
    /// 清空内部缓冲区
    /// 通常在连接重置或错误恢复时使用
    /// </summary>
    public void ClearBuffer() => _buffer.Clear();
    #endregion

    #region 私有方法
    /// <summary>
    /// 从缓冲区中解析完整的数据帧
    /// </summary>
    private void ParseFrames()
    {
        if (_buffer.Length == 0) return;

        int frameEndIndex;
        int frameStartIndex = 0;

        // 循环查找并处理所有完整的数据帧
        while ((frameEndIndex = _buffer.ToString().IndexOf(FrameEndMarker, frameStartIndex, StringComparison.Ordinal)) != -1)
        {
            // 提取一个完整帧（包括结束标记）
            int frameLength = frameEndIndex - frameStartIndex + FrameEndMarker.Length;
            string frameText = _buffer.ToString(frameStartIndex, frameLength);

            // 更新下一帧的起始位置
            frameStartIndex = frameEndIndex + FrameEndMarker.Length;

            // 处理单个帧
            ProcessSingleFrame(frameText);
        }

        // 移除已处理的所有帧
        if (frameStartIndex > 0)
        {
            _buffer.Remove(0, frameStartIndex);
        }
    }

    /// <summary>
    /// 处理单个数据帧
    /// </summary>
    /// <param name="frameText">帧文本</param>
    private void ProcessSingleFrame(string frameText)
    {
        if (string.IsNullOrEmpty(frameText))
        {
            InvalidFrameDetected?.Invoke("接收到空帧", string.Empty);
            return;
        }

        try
        {
            // 移除结束标记并修剪空白字符
            string content = frameText.Replace(FrameEndMarker, string.Empty, StringComparison.Ordinal).Trim();

            if (string.IsNullOrWhiteSpace(content))
            {
                InvalidFrameDetected?.Invoke("帧内容为空", frameText);
                return;
            }

            // 验证帧起始标记
            if (!content.StartsWith(FrameStartMarker, StringComparison.OrdinalIgnoreCase))
            {
                InvalidFrameDetected?.Invoke($"帧必须以{FrameStartMarker}开头", frameText);
                return;
            }

            // 解析传感器信息
            var sensorInfo = ParseSensorInfo(content);
            ValidFrameReceived?.Invoke(frameText, sensorInfo);
        }
        catch (FormatException ex)
        {
            InvalidFrameDetected?.Invoke($"数据格式错误: {ex.Message}", frameText);
        }
        catch (Exception ex)
        {
            InvalidFrameDetected?.Invoke($"解析失败: {ex.Message}", frameText);
        }
    }

    /// <summary>
    /// 解析传感器信息
    /// </summary>
    /// <param name="content">帧内容（不包含起始和结束标记）</param>
    /// <returns>解析后的传感器信息</returns>
    /// <exception cref="FormatException">当数据格式不符合要求时抛出</exception>
    private static SensorInfo ParseSensorInfo(string content)
    {
        // 按空格分割数据项
        string[] parts = content.Split([' '], StringSplitOptions.RemoveEmptyEntries);

        // 验证数据项数量
        if (parts.Length != ExpectedDataItemCount)
            throw new FormatException($"预期{ExpectedDataItemCount}个数据项，实际{parts.Length}个");

        var sensorInfo = new SensorInfo();

        // 解析每个传感器的数据
        for (int i = 0; i < ExpectedSensorCount; i++)
        {
            int startIndex = i * ItemsPerSensor;
            sensorInfo.SensorItemInfos.Add(ParseSensorItemInfo(i, parts, startIndex));
        }

        return sensorInfo;
    }

    /// <summary>
    /// 解析单个传感器项的信息
    /// </summary>
    /// <param name="index">传感器索引</param>
    /// <param name="parts">分割后的数据项数组</param>
    /// <param name="startIndex">当前传感器数据的起始索引</param>
    /// <returns>解析后的传感器项信息</returns>
    /// <exception cref="ArgumentNullException">当parts为null时抛出</exception>
    /// <exception cref="FormatException">当数据格式不符合要求时抛出</exception>
    private static SensorItemInfo ParseSensorItemInfo(int index, string[] parts, int startIndex)
    {
        ArgumentNullException.ThrowIfNull(parts);

        // 验证索引范围
        if (startIndex < 0 || startIndex + ItemsPerSensor - 1 >= parts.Length)
            throw new FormatException($"数据索引超出范围: {startIndex}");

        // 验证传感器标识符格式
        string expectedIdentifier = $"d{index}";
        if (!string.Equals(parts[startIndex], expectedIdentifier, StringComparison.OrdinalIgnoreCase))
            throw new FormatException($"无效标识符: {parts[startIndex]}，预期格式为{expectedIdentifier}");

        // 解析X坐标
        if (!double.TryParse(parts[startIndex + 1], out double x))
            throw new FormatException($"X坐标值解析失败: {parts[startIndex + 1]}");

        // 解析Y坐标
        if (!double.TryParse(parts[startIndex + 2], out double y))
            throw new FormatException($"Y坐标值解析失败: {parts[startIndex + 2]}");

        // 解析Z坐标
        if (!double.TryParse(parts[startIndex + 3], out double z))
            throw new FormatException($"Z坐标值解析失败: {parts[startIndex + 3]}");

        return new SensorItemInfo
        {
            Index = index,
            X = x,
            Y = y,
            Z = z
        };
    }
    #endregion

    #region 资源释放
    /// <summary>
    /// 释放资源
    /// </summary>
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    /// <summary>
    /// 释放资源的具体实现
    /// </summary>
    /// <param name="disposing">是否正在释放托管资源</param>
    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed && disposing)
        {
            _buffer.Clear();
            _disposed = true;
        }
    }
    #endregion
}
